/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.onlyxiahui.wofa.client.net.core.connect;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.onlyxiahui.framework.net.handler.connect.Connector;
import com.onlyxiahui.framework.net.handler.connect.data.ConnectData;
import com.onlyxiahui.framework.net.handler.connect.type.IdleStatus;
import com.onlyxiahui.framework.net.handler.data.handler.MessageHandler;

/**
 * 
 * Description <br>
 * Date 2020-11-15 18:02:33<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
public class WebSocketConnector implements Connector {

	protected final Logger logger = LoggerFactory.getLogger(this.getClass());

	private long sendLastTimestamp = 0;
	private long receiveLastTimestamp = 0;
	private long idleTimestamp = 1000 * 5;

	private WebSocketClient webSocketClient;
	private Draft draft;
	private final int CONNECT_TIMEOUT = 5000;
	private Set<MessageHandler> set = new HashSet<MessageHandler>();

	Object o = null;

	public WebSocketConnector() {
		initConnector();
	}

	private void initConnector() {
		this.draft = new Draft_6455();
	}

	@Override
	public boolean connect(ConnectData connectData) {
		boolean mark;
		try {
			String address = connectData.getAddress();
			URI uri = new URI(address);
			webSocketClient = new WebSocketClient(uri, draft, null, CONNECT_TIMEOUT) {
				@Override
				public void onOpen(ServerHandshake serverHandshake) {
					handleOpen(serverHandshake);
				}

				@Override
				public void onMessage(String message) {
					handleMessage(message);
				}

				@Override
				public void onClose(int code, String reason, boolean remote) {
					handleClose(code, reason, remote);
				}

				@Override
				public void onError(Exception e) {
					handleError(e, o);
				}
			};
//			SSLContext sslContext = SslContextUtil.createSSLContextByClassPath("JKS", "/key/server.jks", "123456");
//			SSLSocketFactory factory = sslContext
//					.getSocketFactory();// (SSLSocketFactory) SSLSocketFactory.getDefault();
//
//			webSocketClient.setSocketFactory(factory);
			mark = webSocketClient.connectBlocking();
		} catch (URISyntaxException ex) {
			mark = false;
		} catch (InterruptedException ex) {
			mark = false;
		} catch (Exception e1) {
			e1.printStackTrace();
			mark = false;
		}
		return mark;
	}

	@Override
	public boolean isConnected() {
		return (null != webSocketClient && webSocketClient.getReadyState() == ReadyState.OPEN);
	}

	@Override
	public boolean write(Object o) {
		boolean mark = isConnected();
		if (mark && null != o) {
			if (o instanceof String) {
				webSocketClient.send(o.toString());
			} else {
				String json = JSONObject.toJSONString(o);
				webSocketClient.send(json);
			}
		}
		this.o = o;
		sendLastTimestamp = System.currentTimeMillis();
		return mark;
	}

	@Override
	public void close() {
		if (null != webSocketClient) {
			webSocketClient.close();
			webSocketClient = null;
		}
	}

	@Override
	public void addMessageHandler(MessageHandler handler) {
		set.add(handler);
	}

	@Override
	public IdleStatus getIdleStatus() {
		long timestamp = System.currentTimeMillis();

		IdleStatus status = IdleStatus.no;
		boolean sendIdle = timestamp - sendLastTimestamp > idleTimestamp;
		boolean receiveIdle = timestamp - receiveLastTimestamp > idleTimestamp;

		if (sendIdle && receiveIdle) {
			status = IdleStatus.all;
		} else if (sendIdle) {
			status = IdleStatus.write;
		} else if (receiveIdle) {
			status = IdleStatus.read;
		}
		return status;
	}

	public void handleOpen(ServerHandshake serverHandshake) {

	}

	public void handleMessage(String message) {
		receiveLastTimestamp = System.currentTimeMillis();
		messageReceived(message);
	}

	public void handleClose(int code, String reason, boolean remote) {

	}

	public void handleError(Exception e, Object o) {

	}

	public void messageReceived(String message) {
		try {
			back(message);
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	void back(Object data) {
		for (MessageHandler h : set) {
			h.receive(data);
		}
	}

	void addExceptionData(String key) {
		for (MessageHandler h : set) {
			h.exceptionData(key);
		}
	}
}
